# -*- coding: utf-8 -*-

# 此处若出现引用错误, 请将src加入PYTHONPATH
from ml.utils import gaussian_kernel
import numpy as np
import logging

"""自己实现的流形排序"""

class ManifoldRanking(object):
    """ranking on data manifolds

    Parameters:
    ----------
    sigma : float
        高斯核
    mu : float
        目标函数的参数

    Attributes:
    ----------
    org_data : array_like, float
        输入的原始数据
    __w : array_like
        相似图的邻接矩阵
    mask : array_like, bool, shape=(n, 1)
        True represents query points, False is ranking points
    """
    def __init__(self, sigma, mu):
        self.__sigma_ = sigma
        self.__mu_ = mu
        self.org_data = np.ndarray
        self.__mask_ = np.ndarray
        self.__w = np.ndarray
        self.__res_ = np.ndarray

    def net_input(self, arr: np.ndarray, mask: np.ndarray):
        """输入数据的预处理
        Parameters:
        ----------
        arr ： array_like, float
            row is the number of data, col is the dimension of data
        mask : array_like, bool, shape=(n, 1)
            True represents query points, False is ranking points
        """
        assert arr.shape[1] == mask.shape[1], "RawNotCorresponding: \n" \
                                              "arr.shape: " + str(arr.shape) \
                                            + " mask.shape: " + str(mask.shape)

        self.org_data = arr
        self.__mask_ = mask
        self.__w = np.empty((arr.shape[0], arr.shape[0]))

    def fit(self):
        self.__net_init()
        d_ = np.diag(np.sum(self.__w, axis=0))
        logging.info("d_.shape: " + str(d_.shape))
        # d_ = np.power(d_, -0.5)
        d_ = np.sqrt(d_)
        e_ = np.eye(d_.shape[0])
        d_ = np.divide(e_, d_, where=d_ != 0)
        s_ = np.dot(d_, np.dot(self.__w, d_))
        logging.info("s_.shape: " + str(s_.shape))
        i_ = np.eye(self.__w.shape[0])
        res = self.__mu_ / (1 + self.__mu_) \
              * np.power(i_ - (1 / (1 + self.__mu_)) * s_, -1)
        self.__res_ = np.dot(res, self.__mask_)
        return self.__res_

    def __net_init(self):
        """新建一个无向赋权图
        使用邻接矩阵的方式构造一个完全图
        """
        for i, e_i in enumerate(self.org_data):
            for j, e_j in enumerate(self.org_data):
                if i == j:
                    self.__w[i][j] = 0.0
                else:
                    self.__w[i][j] = gaussian_kernel(e_i, e_j, self.__sigma_)

    @property
    def category_label(self):
        max_vec = np.max(self.__res_, axis=1).reshape((-1, 1))
        max_vec = np.concatenate((max_vec,)*self.__res_.shape[1], axis=1)
        logging.info("self.__res_.shape: " + str(self.__res_.shape))
        logging.info("max_vec.shape: " + str(max_vec.shape))
        assert max_vec.shape == self.__res_.shape, "ShapeNotSame"
        return np.where(self.__res_ == max_vec, 1, 0)


def config():
    """初始化配置"""
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s'
    )


if __name__ == "__main__":
    config()
    import matplotlib.pyplot as plt
    from ml import datasets

    org_data = datasets.load_double_moon(length=200, dst=-1)
    plt.subplot(121)
    plt.plot(org_data[:,0], org_data[:,1], "g.")
    logging.debug("org_data:")
    logging.debug(org_data)

    # 组装初始类别标签矩阵
    PRIOR_NUM = 5
    CATEGORY_NUM = 2
    temp = np.array([1] * PRIOR_NUM).reshape((-1, 1))
    temp2 = np.array([0] * (org_data.shape[0] - PRIOR_NUM))\
        .reshape((-1, 1))
    mask = np.concatenate(
        (
            np.concatenate((temp, temp2), axis=0),
            np.concatenate((temp2, temp), axis=0)
        ),
        axis=1
    )
    logging.info("mask.shape: " + str(mask.shape))
    logging.debug("mask: " + str(mask))
    mr = ManifoldRanking(3, 0.5)
    mr.net_input(org_data, mask)
    mr.fit()

    res = mr.category_label
    plt.subplot(122)
    for i, e in enumerate(res[:, 0]):
        if e == 1:
            plt.plot(org_data[i, 0], org_data[i, 1], "r+")
        else:
            plt.plot(org_data[i, 0], org_data[i, 1], "b*")
    plt.show()